// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"

#include <fcntl.h>
#include <mach/mach_vm.h>
#include <stddef.h>
#include <sys/mman.h>

#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/mac/mac_util.h"
#include "base/mac/mach_logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/shared_memory.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/spin_wait.h"
#include "base/time/time.h"
#include "ipc/attachment_broker_messages.h"
#include "ipc/attachment_broker_privileged_mac.h"
#include "ipc/attachment_broker_unprivileged_mac.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_test_base.h"
#include "ipc/ipc_test_messages.h"
#include "ipc/test_util_mac.h"

namespace {

const char kDataBuffer1[] = "This is some test data to write to the file.";
const char kDataBuffer2[] = "The lazy dog and a fox.";
const char kDataBuffer3[] = "Two green bears but not a potato.";
const char kDataBuffer4[] = "Red potato is best potato.";
const std::string g_service_switch_name = "service_name";
const size_t g_large_message_size = 8 * 1024 * 1024;
const int g_large_message_count = 1000;
const size_t g_medium_message_size = 512 * 1024;

// Running the message loop is expected to increase the number of resident
// pages. The exact amount is non-deterministic, but for a simple test suite
// like this one, the increase is expected to be less than 1 MB.
const size_t g_expected_memory_increase = 1024 * 1024;

enum TestResult {
    RESULT_UNKNOWN,
    RESULT_SUCCESS,
    RESULT_FAILURE,
};

mach_vm_size_t GetResidentSize()
{
    task_basic_info_64 info;
    mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
    kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO_64,
        reinterpret_cast<task_info_t>(&info), &count);
    MACH_CHECK(kr == KERN_SUCCESS, kr) << "Couldn't get resident size.";

    return info.resident_size;
}

base::mac::ScopedMachSendRight GetMachPortFromBrokeredAttachment(
    const scoped_refptr<IPC::BrokerableAttachment>& attachment)
{
    if (attachment->GetType() != IPC::BrokerableAttachment::TYPE_BROKERABLE_ATTACHMENT) {
        LOG(INFO) << "Attachment type not TYPE_BROKERABLE_ATTACHMENT.";
        return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
    }

    if (attachment->GetBrokerableType() != IPC::BrokerableAttachment::MACH_PORT) {
        LOG(INFO) << "Brokerable type not MACH_PORT.";
        return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
    }

    IPC::internal::MachPortAttachmentMac* received_mach_port_attachment = static_cast<IPC::internal::MachPortAttachmentMac*>(attachment.get());
    base::mac::ScopedMachSendRight send_right(
        received_mach_port_attachment->get_mach_port());
    received_mach_port_attachment->reset_mach_port_ownership();
    return send_right;
}

// Makes a Mach port backed SharedMemory region and fills it with |contents|.
scoped_ptr<base::SharedMemory> MakeSharedMemory(const std::string& contents)
{
    base::SharedMemoryHandle shm(contents.size());
    if (!shm.IsValid()) {
        LOG(ERROR) << "Failed to make SharedMemoryHandle.";
        return nullptr;
    }
    scoped_ptr<base::SharedMemory> shared_memory(
        new base::SharedMemory(shm, false));
    shared_memory->Map(contents.size());
    memcpy(shared_memory->memory(), contents.c_str(), contents.size());
    return shared_memory;
}

// |message| must be deserializable as a TestSharedMemoryHandleMsg1.
base::SharedMemoryHandle GetSharedMemoryHandleFromMsg1(
    const IPC::Message& message)
{
    // Expect a message with a brokered attachment.
    if (!message.HasBrokerableAttachments()) {
        LOG(ERROR) << "Message missing brokerable attachment.";
        return base::SharedMemoryHandle();
    }

    TestSharedMemoryHandleMsg1::Schema::Param p;
    if (!TestSharedMemoryHandleMsg1::Read(&message, &p)) {
        LOG(ERROR) << "Failed to deserialize message.";
        return base::SharedMemoryHandle();
    }

    return base::get<1>(p);
}

// |message| must be deserializable as a TestSharedMemoryHandleMsg2. Returns
// whether deserialization was successful. |handle1| and |handle2| are output
// variables populated on success.
bool GetSharedMemoryHandlesFromMsg2(const IPC::Message& message,
    base::SharedMemoryHandle* handle1,
    base::SharedMemoryHandle* handle2)
{
    // Expect a message with a brokered attachment.
    if (!message.HasBrokerableAttachments()) {
        LOG(ERROR) << "Message missing brokerable attachment.";
        return false;
    }

    TestSharedMemoryHandleMsg2::Schema::Param p;
    if (!TestSharedMemoryHandleMsg2::Read(&message, &p)) {
        LOG(ERROR) << "Failed to deserialize message.";
        return false;
    }

    *handle1 = base::get<0>(p);
    *handle2 = base::get<1>(p);
    return true;
}

// Returns |nullptr| on error.
scoped_ptr<base::SharedMemory> MapSharedMemoryHandle(
    const base::SharedMemoryHandle& shm,
    bool read_only)
{
    if (!shm.IsValid()) {
        LOG(ERROR) << "Invalid SharedMemoryHandle";
        return nullptr;
    }

    size_t size;
    if (!shm.GetSize(&size)) {
        LOG(ERROR) << "Couldn't get size of SharedMemoryHandle";
        return nullptr;
    }

    scoped_ptr<base::SharedMemory> shared_memory(
        new base::SharedMemory(shm, read_only));
    shared_memory->Map(size);
    return shared_memory;
}

// This method maps the SharedMemoryHandle, checks the contents, and then
// consumes a reference to the underlying Mach port.
bool CheckContentsOfSharedMemoryHandle(const base::SharedMemoryHandle& shm,
    const std::string& contents)
{
    scoped_ptr<base::SharedMemory> shared_memory(
        MapSharedMemoryHandle(shm, false));

    if (memcmp(shared_memory->memory(), contents.c_str(), contents.size()) != 0) {
        LOG(ERROR) << "Shared Memory contents not equivalent";
        return false;
    }
    return true;
}

// This method mmaps the FileDescriptor, checks the contents, and then munmaps
// the FileDescriptor and closes the underlying fd.
bool CheckContentsOfFileDescriptor(const base::FileDescriptor& file_descriptor,
    const std::string& contents)
{
    base::ScopedFD fd_closer(file_descriptor.fd);
    lseek(file_descriptor.fd, 0, SEEK_SET);
    scoped_ptr<char, base::FreeDeleter> buffer(
        static_cast<char*>(malloc(contents.size())));
    if (!base::ReadFromFD(file_descriptor.fd, buffer.get(), contents.size()))
        return false;

    int result = memcmp(buffer.get(), contents.c_str(), contents.size());
    return result == 0;
}

// Open |fp| and populate it with |contents|.
base::FileDescriptor MakeFileDescriptor(const base::FilePath& fp,
    const std::string& contents)
{
    int fd = open(fp.value().c_str(), O_RDWR, S_IWUSR | S_IRUSR);
    base::ScopedFD fd_closer(fd);
    if (fd <= 0) {
        LOG(ERROR) << "Error opening file at: " << fp.value();
        return base::FileDescriptor();
    }

    if (lseek(fd, 0, SEEK_SET) != 0) {
        LOG(ERROR) << "Error changing offset";
        return base::FileDescriptor();
    }

    if (write(fd, contents.c_str(), contents.size()) != static_cast<ssize_t>(contents.size())) {
        LOG(ERROR) << "Error writing to file";
        return base::FileDescriptor();
    }

    return base::FileDescriptor(fd_closer.release(), true);
}

// Maps both handles, then checks that their contents matches |contents|. Then
// checks that changes to one are reflected in the other. Then consumes
// references to both underlying Mach ports.
bool CheckContentsOfTwoEquivalentSharedMemoryHandles(
    const base::SharedMemoryHandle& handle1,
    const base::SharedMemoryHandle& handle2,
    const std::string& contents)
{
    scoped_ptr<base::SharedMemory> shared_memory1(
        MapSharedMemoryHandle(handle1, false));
    scoped_ptr<base::SharedMemory> shared_memory2(
        MapSharedMemoryHandle(handle2, false));

    if (memcmp(shared_memory1->memory(), contents.c_str(), contents.size()) != 0) {
        LOG(ERROR) << "Incorrect contents in shared_memory1";
        return false;
    }

    if (memcmp(shared_memory1->memory(), shared_memory2->memory(),
            contents.size())
        != 0) {
        LOG(ERROR) << "Incorrect contents in shared_memory2";
        return false;
    }

    // Updating shared_memory1 should update shared_memory2.
    const char known_string[] = "string bean";
    if (shared_memory1->mapped_size() < strlen(known_string) || shared_memory2->mapped_size() < strlen(known_string)) {
        LOG(ERROR) << "Shared memory size is too small";
        return false;
    }
    memcpy(shared_memory1->memory(), known_string, strlen(known_string));

    if (memcmp(shared_memory1->memory(), shared_memory2->memory(),
            strlen(known_string))
        != 0) {
        LOG(ERROR) << "Incorrect contents in shared_memory2";
        return false;
    }

    return true;
}

// |message| must be deserializable as a TestSharedMemoryHandleMsg1. Returns
// whether the contents of the attached shared memory region matches |contents|.
// Consumes a reference to the underlying Mach port.
bool CheckContentsOfMessage1(const IPC::Message& message,
    const std::string& contents)
{
    base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message));
    return CheckContentsOfSharedMemoryHandle(shm, contents);
}

// Once the test is finished, send a control message to the parent process with
// the result. The message may require the runloop to be run before its
// dispatched.
void SendControlMessage(IPC::Sender* sender, bool success)
{
    IPC::Message* message = new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL);
    TestResult result = success ? RESULT_SUCCESS : RESULT_FAILURE;
    message->WriteInt(result);
    sender->Send(message);
}

// Records the most recently received brokerable attachment's id.
class AttachmentBrokerObserver : public IPC::AttachmentBroker::Observer {
public:
    void ReceivedBrokerableAttachmentWithId(
        const IPC::BrokerableAttachment::AttachmentId& id) override
    {
        id_ = id;
    }
    IPC::BrokerableAttachment::AttachmentId* get_id() { return &id_; }

private:
    IPC::BrokerableAttachment::AttachmentId id_;
};

// A broker which always sets the current process as the destination process
// for attachments.
class MockBroker : public IPC::AttachmentBrokerUnprivilegedMac {
public:
    MockBroker() { }
    ~MockBroker() override { }
    bool SendAttachmentToProcess(
        const scoped_refptr<IPC::BrokerableAttachment>& attachment,
        base::ProcessId destination_process) override
    {
        return IPC::AttachmentBrokerUnprivilegedMac::SendAttachmentToProcess(
            attachment, base::Process::Current().Pid());
    }
};

// Forwards all messages to |listener_|.  Quits the message loop after a
// message is received, or the channel has an error.
class ProxyListener : public IPC::Listener {
public:
    ProxyListener()
        : listener_(nullptr)
        , reason_(MESSAGE_RECEIVED)
    {
    }
    ~ProxyListener() override { }

    // The reason for exiting the message loop.
    enum Reason { MESSAGE_RECEIVED,
        CHANNEL_ERROR };

    bool OnMessageReceived(const IPC::Message& message) override
    {
        bool result = false;
        if (listener_)
            result = listener_->OnMessageReceived(message);
        reason_ = MESSAGE_RECEIVED;
        messages_.push_back(message);
        base::MessageLoop::current()->QuitNow();
        return result;
    }

    void OnChannelError() override
    {
        reason_ = CHANNEL_ERROR;
        base::MessageLoop::current()->QuitNow();
    }

    void set_listener(IPC::Listener* listener) { listener_ = listener; }
    Reason get_reason() { return reason_; }
    IPC::Message get_first_message()
    {
        DCHECK(!messages_.empty());
        return messages_[0];
    }
    void pop_first_message()
    {
        DCHECK(!messages_.empty());
        messages_.erase(messages_.begin());
    }
    bool has_message() { return !messages_.empty(); }

private:
    IPC::Listener* listener_;
    Reason reason_;
    std::vector<IPC::Message> messages_;
};

// Waits for a result to be sent over the channel. Quits the message loop
// after a message is received, or the channel has an error.
class ResultListener : public IPC::Listener {
public:
    ResultListener()
        : result_(RESULT_UNKNOWN)
    {
    }
    ~ResultListener() override { }

    bool OnMessageReceived(const IPC::Message& message) override
    {
        base::PickleIterator iter(message);

        int result;
        EXPECT_TRUE(iter.ReadInt(&result));
        result_ = static_cast<TestResult>(result);
        return true;
    }

    TestResult get_result() { return result_; }

private:
    TestResult result_;
};

class MockPortProvider : public base::PortProvider {
public:
    mach_port_t TaskForPid(base::ProcessHandle process) const override
    {
        auto it = port_map_.find(process);
        if (it != port_map_.end())
            return it->second;
        return MACH_PORT_NULL;
    }

    void InsertEntry(base::ProcessHandle process, mach_port_t task_port)
    {
        port_map_[process] = task_port;
        NotifyObservers(process);
    }

    void ClearPortMap() { port_map_.clear(); }

private:
    std::map<base::ProcessHandle, mach_port_t> port_map_;
};

// End-to-end tests for the attachment brokering process on Mac.
// The parent process acts as an unprivileged process. The child process acts
// as the privileged process.
class IPCAttachmentBrokerMacTest : public IPCTestBase {
public:
    IPCAttachmentBrokerMacTest() { }
    ~IPCAttachmentBrokerMacTest() override { }

    base::CommandLine MakeCmdLine(const std::string& procname) override
    {
        base::CommandLine command_line = IPCTestBase::MakeCmdLine(procname);
        // Pass the service name to the child process.
        command_line.AppendSwitchASCII(g_service_switch_name, service_name_);
        return command_line;
    }

    // Takes ownership of |broker|. Has no effect if called after CommonSetUp().
    void SetBroker(IPC::AttachmentBrokerUnprivilegedMac* broker)
    {
        broker_.reset(broker);
    }

    // Mach Setup that needs to occur before child processes are forked.
    void MachPreForkSetUp()
    {
        service_name_ = IPC::CreateRandomServiceName();
        server_port_.reset(IPC::BecomeMachServer(service_name_.c_str()).release());
    }

    // Mach Setup that needs to occur after child processes are forked.
    void MachPostForkSetUp()
    {
        client_port_.reset(IPC::ReceiveMachPort(server_port_.get()).release());
        IPC::SendMachPort(
            client_port_.get(), mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
    }

    // Setup shared between tests.
    void CommonSetUp(const char* name)
    {
        Init(name);
        MachPreForkSetUp();

        if (!broker_.get())
            SetBroker(new IPC::AttachmentBrokerUnprivilegedMac);

        broker_->AddObserver(&observer_, task_runner());
        CreateChannel(&proxy_listener_);
        broker_->RegisterBrokerCommunicationChannel(channel());
        ASSERT_TRUE(ConnectChannel());
        ASSERT_TRUE(StartClient());

        MachPostForkSetUp();
        active_names_at_start_ = IPC::GetActiveNameCount();
        get_proxy_listener()->set_listener(&result_listener_);
    }

    void CheckChildResult()
    {
        ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED,
            get_proxy_listener()->get_reason());
        ASSERT_EQ(get_result_listener()->get_result(), RESULT_SUCCESS);
    }

    void FinalCleanUp()
    {
        // There should be no leaked names.
        SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(
            base::TimeDelta::FromSeconds(10),
            active_names_at_start_ == IPC::GetActiveNameCount());
        EXPECT_EQ(active_names_at_start_, IPC::GetActiveNameCount());

        // Close the channel so the client's OnChannelError() gets fired.
        channel()->Close();

        EXPECT_TRUE(WaitForClientShutdown());
        DestroyChannel();
        broker_.reset();
    }

    // Teardown shared between most tests.
    void CommonTearDown()
    {
        CheckChildResult();
        FinalCleanUp();
    }

    // Makes a SharedMemory region, fills it with |contents|, sends the handle
    // over Chrome IPC, and unmaps the region.
    void SendMessage1(const std::string& contents)
    {
        scoped_ptr<base::SharedMemory> shared_memory(MakeSharedMemory(contents));
        IPC::Message* message = new TestSharedMemoryHandleMsg1(100, shared_memory->handle(), 200);
        sender()->Send(message);
    }

    ProxyListener* get_proxy_listener() { return &proxy_listener_; }
    IPC::AttachmentBrokerUnprivilegedMac* get_broker() { return broker_.get(); }
    AttachmentBrokerObserver* get_observer() { return &observer_; }
    ResultListener* get_result_listener() { return &result_listener_; }

protected:
    // The number of active names immediately after set up.
    mach_msg_type_number_t active_names_at_start_;

private:
    ProxyListener proxy_listener_;
    scoped_ptr<IPC::AttachmentBrokerUnprivilegedMac> broker_;
    AttachmentBrokerObserver observer_;

    // A port on which the main process listens for mach messages from the child
    // process.
    base::mac::ScopedMachReceiveRight server_port_;

    // A port on which the child process listens for mach messages from the main
    // process.
    base::mac::ScopedMachSendRight client_port_;

    std::string service_name_;

    ResultListener result_listener_;
};

// These objects are globally accessible, and are expected to outlive all IPC
// Channels.
struct ChildProcessGlobals {
    MockPortProvider port_provider;

    // The broker must be destroyed before the port_provider, so that the broker
    // gets a chance to unregister itself as an observer. This doesn't matter
    // outside of tests, since neither port_provider nor broker will ever be
    // destroyed.
    scoped_ptr<IPC::AttachmentBrokerPrivilegedMac> broker;
    base::mac::ScopedMachSendRight server_task_port;

    // Total resident memory before running the message loop.
    mach_vm_size_t initial_resident_size;

    // Whether to emit log statements while processing messages.
    bool message_logging;
};

using OnMessageReceivedCallback = void (*)(IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals);

// Sets up the Mach communication ports with the server. Returns a set of
// globals that must live at least as long as the test.
scoped_ptr<ChildProcessGlobals> CommonChildProcessSetUp()
{
    base::CommandLine cmd_line = *base::CommandLine::ForCurrentProcess();
    std::string service_name = cmd_line.GetSwitchValueASCII(g_service_switch_name);
    base::mac::ScopedMachSendRight server_port(
        IPC::LookupServer(service_name.c_str()));
    base::mac::ScopedMachReceiveRight client_port(IPC::MakeReceivingPort());

    // Send the port that this process is listening on to the server.
    IPC::SendMachPort(
        server_port.get(), client_port.get(), MACH_MSG_TYPE_MAKE_SEND);

    // Receive the task port of the server process.
    base::mac::ScopedMachSendRight server_task_port(
        IPC::ReceiveMachPort(client_port.get()));

    scoped_ptr<ChildProcessGlobals> globals(new ChildProcessGlobals);
    globals->broker.reset(
        new IPC::AttachmentBrokerPrivilegedMac(&globals->port_provider));
    globals->port_provider.InsertEntry(getppid(), server_task_port.get());
    globals->server_task_port.reset(server_task_port.release());
    globals->message_logging = true;
    return globals;
}

int CommonPrivilegedProcessMain(OnMessageReceivedCallback callback,
    const char* channel_name)
{
    LOG(INFO) << "Privileged process start.";
    scoped_ptr<ChildProcessGlobals> globals(CommonChildProcessSetUp());

    mach_msg_type_number_t active_names_at_start = IPC::GetActiveNameCount();

    base::MessageLoopForIO main_message_loop;
    ProxyListener listener;

    scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient(
        IPCTestBase::GetChannelName(channel_name), &listener));
    globals->broker->RegisterCommunicationChannel(channel.get());
    CHECK(channel->Connect());

    globals->initial_resident_size = GetResidentSize();

    while (true) {
        if (globals->message_logging)
            LOG(INFO) << "Privileged process spinning run loop.";
        base::MessageLoop::current()->Run();
        ProxyListener::Reason reason = listener.get_reason();
        if (reason == ProxyListener::CHANNEL_ERROR)
            break;

        while (listener.has_message()) {
            if (globals->message_logging)
                LOG(INFO) << "Privileged process running callback.";
            callback(channel.get(), listener.get_first_message(), globals.get());
            if (globals->message_logging)
                LOG(INFO) << "Privileged process finishing callback.";
            listener.pop_first_message();
        }
    }

    if (active_names_at_start != IPC::GetActiveNameCount()) {
        LOG(INFO) << "Memory leak!.";
    }
    LOG(INFO) << "Privileged process end.";
    return 0;
}

// An unprivileged process makes a shared memory region, and writes a string to
// it. The SharedMemoryHandle is sent to the privileged process using Chrome
// IPC. The privileged process checks that it received the same memory region.
TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandle)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("SendSharedMemoryHandle");

    SendMessage1(kDataBuffer1);
    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void SendSharedMemoryHandleCallback(IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    bool success = CheckContentsOfMessage1(message, kDataBuffer1);
    SendControlMessage(sender, success);
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandle)
{
    return CommonPrivilegedProcessMain(&SendSharedMemoryHandleCallback,
        "SendSharedMemoryHandle");
}

// Similar to SendSharedMemoryHandle, but sends a very long shared memory
// region.
TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleLong)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("SendSharedMemoryHandleLong");

    std::string buffer(1 << 23, 'a');
    SendMessage1(buffer);
    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void SendSharedMemoryHandleLongCallback(IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    std::string buffer(1 << 23, 'a');
    bool success = CheckContentsOfMessage1(message, buffer);
    SendControlMessage(sender, success);
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleLong)
{
    return CommonPrivilegedProcessMain(&SendSharedMemoryHandleLongCallback,
        "SendSharedMemoryHandleLong");
}

// Similar to SendSharedMemoryHandle, but sends two different shared memory
// regions in two messages.
TEST_F(IPCAttachmentBrokerMacTest, SendTwoMessagesDifferentSharedMemoryHandle)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("SendTwoMessagesDifferentSharedMemoryHandle");

    SendMessage1(kDataBuffer1);
    SendMessage1(kDataBuffer2);
    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void SendTwoMessagesDifferentSharedMemoryHandleCallback(
    IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    static int count = 0;
    static bool success = true;
    ++count;
    if (count == 1) {
        success &= CheckContentsOfMessage1(message, kDataBuffer1);
    } else if (count == 2) {
        success &= CheckContentsOfMessage1(message, kDataBuffer2);
        SendControlMessage(sender, success);
    }
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendTwoMessagesDifferentSharedMemoryHandle)
{
    return CommonPrivilegedProcessMain(
        &SendTwoMessagesDifferentSharedMemoryHandleCallback,
        "SendTwoMessagesDifferentSharedMemoryHandle");
}

// Similar to SendSharedMemoryHandle, but sends the same shared memory region in
// two messages.
TEST_F(IPCAttachmentBrokerMacTest, SendTwoMessagesSameSharedMemoryHandle)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("SendTwoMessagesSameSharedMemoryHandle");

    {
        scoped_ptr<base::SharedMemory> shared_memory(
            MakeSharedMemory(kDataBuffer1));

        for (int i = 0; i < 2; ++i) {
            IPC::Message* message = new TestSharedMemoryHandleMsg1(100, shared_memory->handle(), 200);
            sender()->Send(message);
        }
    }

    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void SendTwoMessagesSameSharedMemoryHandleCallback(
    IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    static int count = 0;
    static base::SharedMemoryHandle handle1;
    ++count;

    if (count == 1) {
        handle1 = GetSharedMemoryHandleFromMsg1(message);
    } else if (count == 2) {
        base::SharedMemoryHandle handle2(GetSharedMemoryHandleFromMsg1(message));

        bool success = CheckContentsOfTwoEquivalentSharedMemoryHandles(
            handle1, handle2, kDataBuffer1);
        SendControlMessage(sender, success);
    }
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendTwoMessagesSameSharedMemoryHandle)
{
    return CommonPrivilegedProcessMain(
        &SendTwoMessagesSameSharedMemoryHandleCallback,
        "SendTwoMessagesSameSharedMemoryHandle");
}

// Similar to SendSharedMemoryHandle, but sends one message with two different
// memory regions.
TEST_F(IPCAttachmentBrokerMacTest,
    SendOneMessageWithTwoDifferentSharedMemoryHandles)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("SendOneMessageWithTwoDifferentSharedMemoryHandles");

    {
        scoped_ptr<base::SharedMemory> shared_memory1(
            MakeSharedMemory(kDataBuffer1));
        scoped_ptr<base::SharedMemory> shared_memory2(
            MakeSharedMemory(kDataBuffer2));
        IPC::Message* message = new TestSharedMemoryHandleMsg2(
            shared_memory1->handle(), shared_memory2->handle());
        sender()->Send(message);
    }
    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void SendOneMessageWithTwoDifferentSharedMemoryHandlesCallback(
    IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    base::SharedMemoryHandle handle1;
    base::SharedMemoryHandle handle2;
    if (!GetSharedMemoryHandlesFromMsg2(message, &handle1, &handle2)) {
        LOG(ERROR) << "Failed to deserialize message.";
        SendControlMessage(sender, false);
        return;
    }

    bool success = CheckContentsOfSharedMemoryHandle(handle1, kDataBuffer1) && CheckContentsOfSharedMemoryHandle(handle2, kDataBuffer2);
    SendControlMessage(sender, success);
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(
    SendOneMessageWithTwoDifferentSharedMemoryHandles)
{
    return CommonPrivilegedProcessMain(
        &SendOneMessageWithTwoDifferentSharedMemoryHandlesCallback,
        "SendOneMessageWithTwoDifferentSharedMemoryHandles");
}

// Similar to SendSharedMemoryHandle, but sends one message that contains the
// same memory region twice.
TEST_F(IPCAttachmentBrokerMacTest,
    SendOneMessageWithTwoSameSharedMemoryHandles)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("SendOneMessageWithTwoSameSharedMemoryHandles");

    {
        scoped_ptr<base::SharedMemory> shared_memory(
            MakeSharedMemory(kDataBuffer1));
        IPC::Message* message = new TestSharedMemoryHandleMsg2(
            shared_memory->handle(), shared_memory->handle());
        sender()->Send(message);
    }
    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void SendOneMessageWithTwoSameSharedMemoryHandlesCallback(
    IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    base::SharedMemoryHandle handle1;
    base::SharedMemoryHandle handle2;
    if (!GetSharedMemoryHandlesFromMsg2(message, &handle1, &handle2)) {
        LOG(ERROR) << "Failed to deserialize message.";
        SendControlMessage(sender, false);
        return;
    }

    bool success = CheckContentsOfTwoEquivalentSharedMemoryHandles(
        handle1, handle2, kDataBuffer1);
    SendControlMessage(sender, success);
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(
    SendOneMessageWithTwoSameSharedMemoryHandles)
{
    return CommonPrivilegedProcessMain(
        &SendOneMessageWithTwoSameSharedMemoryHandlesCallback,
        "SendOneMessageWithTwoSameSharedMemoryHandles");
}

// Sends one message with two Posix FDs and two Mach ports.
TEST_F(IPCAttachmentBrokerMacTest, SendPosixFDAndMachPort)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    base::ScopedTempDir temp_dir;
    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
    base::FilePath fp1, fp2;
    ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &fp1));
    ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &fp2));

    CommonSetUp("SendPosixFDAndMachPort");

    {
        scoped_ptr<base::SharedMemory> shared_memory1(
            MakeSharedMemory(kDataBuffer1));
        scoped_ptr<base::SharedMemory> shared_memory2(
            MakeSharedMemory(kDataBuffer2));

        base::FileDescriptor file_descriptor1(
            MakeFileDescriptor(fp1, kDataBuffer3));
        base::FileDescriptor file_descriptor2(
            MakeFileDescriptor(fp2, kDataBuffer4));

        IPC::Message* message = new TestSharedMemoryHandleMsg3(
            file_descriptor1, shared_memory1->handle(), file_descriptor2,
            shared_memory2->handle());
        sender()->Send(message);
    }

    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void SendPosixFDAndMachPortCallback(IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    TestSharedMemoryHandleMsg3::Schema::Param p;
    if (!TestSharedMemoryHandleMsg3::Read(&message, &p)) {
        LOG(ERROR) << "Failed to deserialize message.";
        SendControlMessage(sender, false);
        return;
    }

    base::SharedMemoryHandle handle1 = base::get<1>(p);
    base::SharedMemoryHandle handle2 = base::get<3>(p);
    bool success1 = CheckContentsOfSharedMemoryHandle(handle1, kDataBuffer1) && CheckContentsOfSharedMemoryHandle(handle2, kDataBuffer2);
    if (!success1)
        LOG(ERROR) << "SharedMemoryHandles have wrong contents.";

    bool success2 = CheckContentsOfFileDescriptor(base::get<0>(p), kDataBuffer3) && CheckContentsOfFileDescriptor(base::get<2>(p), kDataBuffer4);
    if (!success2)
        LOG(ERROR) << "FileDescriptors have wrong contents.";

    SendControlMessage(sender, success1 && success2);
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendPosixFDAndMachPort)
{
    return CommonPrivilegedProcessMain(&SendPosixFDAndMachPortCallback,
        "SendPosixFDAndMachPort");
}

// Similar to SendHandle, except the attachment's destination process is this
// process. This is an unrealistic scenario, but simulates an unprivileged
// process sending an attachment to another unprivileged process.
TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleToSelf)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    SetBroker(new MockBroker);
    CommonSetUp("SendSharedMemoryHandleToSelf");

    // Technically, the channel is an endpoint, but we need the proxy listener to
    // receive the messages so that it can quit the message loop.
    channel()->SetAttachmentBrokerEndpoint(false);
    get_proxy_listener()->set_listener(get_broker());

    {
        scoped_ptr<base::SharedMemory> shared_memory(
            MakeSharedMemory(kDataBuffer1));
        mach_port_urefs_t ref_count = IPC::GetMachRefCount(
            shared_memory->handle().GetMemoryObject(), MACH_PORT_RIGHT_SEND);

        IPC::Message* message = new TestSharedMemoryHandleMsg1(100, shared_memory->handle(), 200);
        sender()->Send(message);

        // Wait until the child process has sent this process a message.
        base::MessageLoop::current()->Run();

        // Wait for any asynchronous activity to complete.
        base::MessageLoop::current()->RunUntilIdle();

        // Get the received attachment.
        IPC::BrokerableAttachment::AttachmentId* id = get_observer()->get_id();
        ASSERT_TRUE(id);
        scoped_refptr<IPC::BrokerableAttachment> received_attachment;
        get_broker()->GetAttachmentWithId(*id, &received_attachment);
        ASSERT_NE(received_attachment.get(), nullptr);

        // Check that it's has the same name, but that the ref count has increased.
        base::mac::ScopedMachSendRight memory_object(
            GetMachPortFromBrokeredAttachment(received_attachment));
        ASSERT_EQ(memory_object, shared_memory->handle().GetMemoryObject());
        EXPECT_EQ(ref_count + 1,
            IPC::GetMachRefCount(shared_memory->handle().GetMemoryObject(),
                MACH_PORT_RIGHT_SEND));
    }

    FinalCleanUp();
}

void SendSharedMemoryHandleToSelfCallback(IPC::Sender* sender,
    const IPC::Message&,
    ChildProcessGlobals* globals)
{
    // Do nothing special. The default behavior already runs the
    // AttachmentBrokerPrivilegedMac.
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleToSelf)
{
    return CommonPrivilegedProcessMain(&SendSharedMemoryHandleToSelfCallback,
        "SendSharedMemoryHandleToSelf");
}

// Similar to SendSharedMemoryHandle, but uses a ChannelProxy instead of a
// Channel.
TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleChannelProxy)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    Init("SendSharedMemoryHandleChannelProxy");
    MachPreForkSetUp();

    SetBroker(new IPC::AttachmentBrokerUnprivilegedMac);
    get_broker()->AddObserver(get_observer(), task_runner());

    scoped_ptr<base::Thread> thread(
        new base::Thread("ChannelProxyTestServerThread"));
    base::Thread::Options options;
    options.message_loop_type = base::MessageLoop::TYPE_IO;
    thread->StartWithOptions(options);

    CreateChannelProxy(get_proxy_listener(), thread->task_runner().get());
    get_broker()->RegisterBrokerCommunicationChannel(channel_proxy());

    ASSERT_TRUE(StartClient());

    MachPostForkSetUp();
    active_names_at_start_ = IPC::GetActiveNameCount();
    get_proxy_listener()->set_listener(get_result_listener());

    SendMessage1(kDataBuffer1);
    base::MessageLoop::current()->Run();

    CheckChildResult();

    // There should be no leaked names.
    EXPECT_EQ(active_names_at_start_, IPC::GetActiveNameCount());

    // Close the channel so the client's OnChannelError() gets fired.
    channel_proxy()->Close();

    EXPECT_TRUE(WaitForClientShutdown());
    DestroyChannelProxy();
}

void SendSharedMemoryHandleChannelProxyCallback(IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    bool success = CheckContentsOfMessage1(message, kDataBuffer1);
    SendControlMessage(sender, success);
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleChannelProxy)
{
    return CommonPrivilegedProcessMain(
        &SendSharedMemoryHandleChannelProxyCallback,
        "SendSharedMemoryHandleChannelProxy");
}

// Similar to SendSharedMemoryHandle, but first makes a copy of the handle using
// ShareToProcess().
TEST_F(IPCAttachmentBrokerMacTest, ShareToProcess)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("ShareToProcess");

    {
        scoped_ptr<base::SharedMemory> shared_memory(
            MakeSharedMemory(kDataBuffer1));
        base::SharedMemoryHandle new_handle;
        ASSERT_TRUE(shared_memory->ShareToProcess(0, &new_handle));
        IPC::Message* message = new TestSharedMemoryHandleMsg1(100, new_handle, 200);
        sender()->Send(message);
    }

    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void ShareToProcessCallback(IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    bool success = CheckContentsOfMessage1(message, kDataBuffer1);
    SendControlMessage(sender, success);
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(ShareToProcess)
{
    return CommonPrivilegedProcessMain(&ShareToProcessCallback, "ShareToProcess");
}

// Similar to ShareToProcess, but instead shares the memory object only with
// read permissions.
TEST_F(IPCAttachmentBrokerMacTest, ShareReadOnlyToProcess)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("ShareReadOnlyToProcess");

    {
        scoped_ptr<base::SharedMemory> shared_memory(
            MakeSharedMemory(kDataBuffer1));
        base::SharedMemoryHandle new_handle;
        ASSERT_TRUE(shared_memory->ShareReadOnlyToProcess(0, &new_handle));
        IPC::Message* message = new TestSharedMemoryHandleMsg1(100, new_handle, 200);
        sender()->Send(message);
    }

    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void ShareReadOnlyToProcessCallback(IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message));

    // Try to map the memory as writable.
    scoped_ptr<base::SharedMemory> shared_memory(
        MapSharedMemoryHandle(shm, false));
    ASSERT_EQ(nullptr, shared_memory->memory());

    // Now try as read-only.
    scoped_ptr<base::SharedMemory> shared_memory2(
        MapSharedMemoryHandle(shm.Duplicate(), true));
    int current_prot, max_prot;
    ASSERT_TRUE(IPC::GetMachProtections(shared_memory2->memory(),
        shared_memory2->mapped_size(),
        &current_prot, &max_prot));
    ASSERT_EQ(VM_PROT_READ, current_prot);
    ASSERT_EQ(VM_PROT_READ, max_prot);

    bool success = memcmp(shared_memory2->memory(), kDataBuffer1, strlen(kDataBuffer1)) == 0;
    SendControlMessage(sender, success);
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(ShareReadOnlyToProcess)
{
    return CommonPrivilegedProcessMain(&ShareReadOnlyToProcessCallback,
        "ShareReadOnlyToProcess");
}

// Similar to SendSharedMemoryHandleToSelf, but the child process pretends to
// not have the task port for the parent process.
TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleToSelfDelayedPort)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    SetBroker(new MockBroker);
    CommonSetUp("SendSharedMemoryHandleToSelfDelayedPort");

    // Technically, the channel is an endpoint, but we need the proxy listener to
    // receive the messages so that it can quit the message loop.
    channel()->SetAttachmentBrokerEndpoint(false);
    get_proxy_listener()->set_listener(get_broker());

    {
        scoped_ptr<base::SharedMemory> shared_memory(
            MakeSharedMemory(kDataBuffer1));
        mach_port_urefs_t ref_count = IPC::GetMachRefCount(
            shared_memory->handle().GetMemoryObject(), MACH_PORT_RIGHT_SEND);

        std::vector<IPC::BrokerableAttachment::AttachmentId> ids;
        const int kMessagesToTest = 3;
        for (int i = 0; i < kMessagesToTest; ++i) {
            base::SharedMemoryHandle h = shared_memory->handle().Duplicate();
            ids.push_back(
                IPC::BrokerableAttachment::AttachmentId::CreateIdWithRandomNonce());
            IPC::internal::MachPortAttachmentMac::WireFormat wire_format(
                h.GetMemoryObject(), getpid(), ids[i]);
            sender()->Send(new AttachmentBrokerMsg_DuplicateMachPort(wire_format));

            // Send a dummy message, which will trigger the callback handler in the
            // child process.
            sender()->Send(new TestSharedMemoryHandleMsg4(1));
        }

        int received_message_count = 0;
        while (received_message_count < kMessagesToTest) {
            // Wait until the child process has sent this process a message.
            base::MessageLoop::current()->Run();

            // Wait for any asynchronous activity to complete.
            base::MessageLoop::current()->RunUntilIdle();

            while (get_proxy_listener()->has_message()) {
                get_proxy_listener()->pop_first_message();
                received_message_count++;
            }
        }

        for (int i = 0; i < kMessagesToTest; ++i) {
            IPC::BrokerableAttachment::AttachmentId* id = &ids[i];
            ASSERT_TRUE(id);
            scoped_refptr<IPC::BrokerableAttachment> received_attachment;
            get_broker()->GetAttachmentWithId(*id, &received_attachment);
            ASSERT_NE(received_attachment.get(), nullptr);

            base::mac::ScopedMachSendRight memory_object(
                GetMachPortFromBrokeredAttachment(received_attachment));
            ASSERT_EQ(shared_memory->handle().GetMemoryObject(), memory_object);
        }

        // Check that the ref count hasn't changed.
        EXPECT_EQ(ref_count,
            IPC::GetMachRefCount(shared_memory->handle().GetMemoryObject(),
                MACH_PORT_RIGHT_SEND));
    }

    FinalCleanUp();
}

void SendSharedMemoryHandleToSelfDelayedPortCallback(
    IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    static int i = 0;
    static base::ProcessId pid = message.get_sender_pid();
    static mach_port_t task_port = globals->port_provider.TaskForPid(pid);
    ++i;

    if (i == 1) {
        // Pretend to not have the task port for the parent.
        globals->port_provider.ClearPortMap();
    } else if (i == 2) {
        // Intentionally do nothing.
    } else if (i == 3) {
        // Setting the task port should trigger callbacks, eventually resulting in
        // multiple attachment broker messages.
        globals->port_provider.InsertEntry(pid, task_port);
    }
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleToSelfDelayedPort)
{
    return CommonPrivilegedProcessMain(
        &SendSharedMemoryHandleToSelfDelayedPortCallback,
        "SendSharedMemoryHandleToSelfDelayedPort");
}

// Tests the memory usage characteristics of attachment brokering a single large
// message. This test has the *potential* to be flaky, since it compares
// resident memory at different points in time, and that measurement is
// non-deterministic.
TEST_F(IPCAttachmentBrokerMacTest, MemoryUsageLargeMessage)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("MemoryUsageLargeMessage");

    std::string test_string(g_large_message_size, 'a');
    SendMessage1(test_string);
    base::MessageLoop::current()->Run();
    CommonTearDown();
}

void MemoryUsageLargeMessageCallback(IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    EXPECT_LE(GetResidentSize(),
        globals->initial_resident_size + g_expected_memory_increase);

    base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message));
    scoped_ptr<base::SharedMemory> shared_memory(
        MapSharedMemoryHandle(shm, false));
    EXPECT_LE(GetResidentSize(),
        globals->initial_resident_size + g_expected_memory_increase);

    char* addr = static_cast<char*>(shared_memory->memory());
    for (size_t i = 0; i < g_large_message_size; i += 1024) {
        addr[i] = 'a';
    }
    EXPECT_GE(GetResidentSize(),
        globals->initial_resident_size + g_large_message_size);

    shared_memory.reset();
#if !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && !defined(MEMORY_SANITIZER) && !defined(THREAD_SANITIZER) && !defined(UNDEFINED_SANITIZER)
    // Under a sanitizer build, releasing memory does not necessarily reduce the
    // amount of resident memory.
    EXPECT_LE(GetResidentSize(),
        globals->initial_resident_size + g_expected_memory_increase);
#endif

    SendControlMessage(sender, true);
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(MemoryUsageLargeMessage)
{
    return CommonPrivilegedProcessMain(&MemoryUsageLargeMessageCallback,
        "MemoryUsageLargeMessage");
}

// Tests the memory usage characteristics of attachment brokering many small
// messages. This test has the *potential* to be flaky, since it compares
// resident memory at different points in time, and that measurement is
// non-deterministic.
TEST_F(IPCAttachmentBrokerMacTest, MemoryUsageManyMessages)
{
    // Mach-based SharedMemory isn't support on OSX 10.6.
    if (base::mac::IsOSSnowLeopard())
        return;

    CommonSetUp("MemoryUsageManyMessages");

    for (int i = 0; i < g_large_message_count; ++i) {
        std::string message = base::IntToString(i);
        message += '\0';
        size_t end = message.size();
        message.resize(g_medium_message_size);
        std::fill(message.begin() + end, message.end(), 'a');
        SendMessage1(message);

        base::MessageLoop::current()->RunUntilIdle();
    }

    if (get_result_listener()->get_result() == RESULT_UNKNOWN)
        base::MessageLoop::current()->Run();

    CommonTearDown();
}

void MemoryUsageManyMessagesCallback(IPC::Sender* sender,
    const IPC::Message& message,
    ChildProcessGlobals* globals)
{
    static int message_index = 0;

    {
        // Map the shared memory, and make sure that its pages are counting towards
        // resident size.
        base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message));
        scoped_ptr<base::SharedMemory> shared_memory(
            MapSharedMemoryHandle(shm, false));

        char* addr = static_cast<char*>(shared_memory->memory());
        std::string message_string(addr);
        int message_int;
        ASSERT_TRUE(base::StringToInt(message_string, &message_int));
        ASSERT_EQ(message_index, message_int);
        for (size_t i = 0; i < g_medium_message_size; i += 1024) {
            addr[i] = 'a';
        }
    }

    ++message_index;

    if (message_index == 1) {
        // Disable message logging, since it significantly contributes towards total
        // memory usage.
        LOG(INFO) << "Disable privileged process message logging.";
        globals->message_logging = false;
    }

    if (message_index == g_large_message_count) {
        size_t memory_increase_kb = (GetResidentSize() - globals->initial_resident_size) / 1024;
        LOG(INFO) << "Increase in memory usage in KB: " << memory_increase_kb;

#if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || defined(UNDEFINED_SANITIZER)
        // Under a sanitizer build, releasing memory does not necessarily reduce the
        // amount of resident memory.
        bool success = true;
#else
        // The total increase in resident size should be less than 1MB. The exact
        // amount is not deterministic.
        bool success = memory_increase_kb < 1024;
#endif

        SendControlMessage(sender, success);
    }
}

MULTIPROCESS_IPC_TEST_CLIENT_MAIN(MemoryUsageManyMessages)
{
    return CommonPrivilegedProcessMain(&MemoryUsageManyMessagesCallback,
        "MemoryUsageManyMessages");
}

} // namespace
